package com.daji.io_nio.channel;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/*
Channel概述：
    通过 channel 我们可以操作数据源，但
    又不必关心数据源的具体物理结构。这个数据源可能是多种的。比如，可以是文件，
    也可以是网络 socket。在大多数应用中，channel 与文件描述符或者 socket 是一一
    对应的。Channel 用于在字节缓冲区和位于通道另一侧的实体（通常是一个文件或套
    接字）之间有效地传输数据。

    注意，这里说的是字节，而不是字符。也就是说这个channel可以传任何文件。
    字节和字符区别，可以去查：字节流可以处理一切文件(网络传输)，而字符流只能处理纯文本文件。
    Java中最基本的两个字节流类是InputStream和OutputStream,而字符流是Writer、Reader这两个类

    Channel既能读，又能写,是全双工的。下面分两个demo演示：
    A、通过FileChannel读取文件中数据到Buffer中
    B、FileChannel读取Buffer的数据,然后FileChannel将该数据回写给文件
*/
public class FileChannelAndBufferDemo1 {
    public static void main(String[] args) throws IOException {
        //A和B可以注释掉一个，看另一个的效果。

        //A、通过FileChannel读取文件中数据到Buffer中
        fileChannelReadFileToBuffer();
        //B、FileChannel读取Buffer的数据,然后FileChannel将该数据回写给文件
        BufferWriteDataToChannel();
    }

    //A、FileChannel写入Buffer数据(数据就是文件的数据)
    public static void fileChannelReadFileToBuffer() throws IOException {
        //1、创建FileChannel(通过文件创建)：我们无法直接创建一个
        //FileChannel，需要通过使用一个 InputStream、OutputStream 或
        //RandomAccessFile 来获取一个 FileChannel 实例。
        RandomAccessFile aFile = new
                RandomAccessFile("/home/daji/tempfiles/TestFileChannel.txt", "rw");    //第二个参数代表模式 rw就是读写模式
        FileChannel inChannel = aFile.getChannel();
        //2、创建buffer
        ByteBuffer buf = ByteBuffer.allocate(20);   //allocate分配内存空间 现在分配了48个单位的内存
        //buffer如何在缓冲区读取数据，跟断点一跟就知道。看一下上面的allocate设置为2 和 设置为20 的区别(观察下面的双层while循环变化)
        //buffer如何在缓冲区读取数据，跟断点一跟就知道。看一下上面的allocate设置为2 和 设置为20 的区别(观察下面的双层while循环变化)
        //buffer如何在缓冲区读取数据，跟断点一跟就知道。看一下上面的allocate设置为2 和 设置为20 的区别(观察下面的双层while循环变化)
        //buffer如何在缓冲区读取数据，跟断点一跟就知道。看一下上面的allocate设置为2 和 设置为20 的区别(观察下面的双层while循环变化)
        //buffer如何在缓冲区读取数据，跟断点一跟就知道。看一下上面的allocate设置为2 和 设置为20 的区别(观察下面的双层while循环变化)
        //buffer如何在缓冲区读取数据，跟断点一跟就知道。看一下上面的allocate设置为2 和 设置为20 的区别(观察下面的双层while循环变化)
        /*
            我跟了一遍断点。实际上的原理是：会根据上面allocate设置的缓冲区大小(比如说我设置为20)，一次性读取20个
            字节(byte)，然后读写转换，将这20个字节写出去。然后再继续读取20个字节...直到把文件全读完为止。
            综上，buffer就是个缓冲区

            补充：
            字节(Byte)是计量单位，表示数据量多少，是计算机信息技术用于计量存储容量的一种计量单位，通常情况下一字节等于八位。
            字符(Character)计算机中使用的字母、数字、字和符号，比如'A'、'B'、'$'、'&'等。
            UTF-8 编码中，一个英文字为一个字节，一个中文为三个字节。
            Unicode 编码中，一个英文为一个字节，一个中文为两个字节。
         */

        //3、读取数据到buffer中
        int bytesRead = inChannel.read(buf);    //read方法返回的int表示有多少个字节被读到了buffer
        while (bytesRead != -1) {   //如果读到了-1,代表文件结束了(就这么规定的)
            System.out.println("读取： " + bytesRead);
            buf.flip();//读写转换(flip意为翻转)
            while (buf.hasRemaining()) {
                //动手试一试：这里如果强制类型转换不是char，而是byte呢?会怎么样
                //查一下byte 和 char的区别
                /*
                    补充：
                    字节(Byte)是计量单位，表示数据量多少，是计算机信息技术用于计量存储容量的一种计量单位，通常情况下一字节等于八位。
                    字符(Character)计算机中使用的字母、数字、字和符号，比如'A'、'B'、'$'、'&'等。
                    UTF-8 编码中，一个英文字为一个字节，一个中文为三个字节。
                    Unicode 编码中，一个英文为一个字节，一个中文为两个字节。
                */
                System.out.print((char) buf.get()); //实际上是逐字符读取
            }
            buf.clear();
            bytesRead = inChannel.read(buf);
        }
        aFile.close();//用完channel，记得关闭该channel
        System.out.println("操作结束");
    }

    //B、FileChannel读取Buffer的数据,然后FileChannel将该数据回写给文件
    public static void BufferWriteDataToChannel() throws IOException {
        RandomAccessFile aFile = new
                RandomAccessFile("/home/daji/tempfiles/TestFileChannel.txt", "rw");
        FileChannel inChannel = aFile.getChannel();
        String newData = "now, put this string to file..." +
                System.currentTimeMillis();
        ByteBuffer buf1 = ByteBuffer.allocate(48);
        buf1.clear();
        buf1.put(newData.getBytes());   //此时的buffer就是上面那句话
        buf1.flip();
        while (buf1.hasRemaining()) {
            inChannel.write(buf1);  //将buffer里面全部数据传进channel里面，channel会反写给文件
        }
        inChannel.close();
        //代码执行过后，打开你的文件，看看是不是回写了“now, put this string to file...” 这样一句话
    }

}